home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 2: CDPD 1
/
Almathera Ten on Ten - Disc 2: CDPD 1.iso
/
pd
/
076-100
/
084
/
audiotools
/
audiotools.c
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
16KB
|
627 lines
/* audiotools.c */
#define DEBUG 1
#include "exec/types.h"
#include "exec/memory.h"
#include "devices/audio.h"
#include "ram:audiotools.h"
#include "ram:globals.c"
extern APTR AllocMem();
extern struct Message *GetMsg();
main()
{
LONG i, channel, error;
InitAudio();
for(i=0; i<4; i++)
{ channel = GetChannel(-1);
if(channel == -1) finishup("cannot get a channel!");
/* At this point, must save globals from gotkey, gotunit */
key[i] = gotkey; /* save allocation key */
unit[i] = gotunit; /* save unit value */
error = StopChannel(channel);
if(error)
{ printf("error in stopping channel = %ld\n",error);
finishup("StopChannel did not work as expected");
}
}
/* (channel, note, waveform, vol, duration, priority,message) */
for(i=0; i<95; i++)
{
PlayNote(0, i, w1, 32, 250, 0, 0); /* all notes, 1/4 sec. */
}
error = StartChannel(0);
Delay(800); /* let most of them play... this waits 16 seconds */
for(i=1; i<4; i++)
{ error = StartChannel(i);
if(error) printf("error starting channel = %ld\n",error);
}
PlayNote(0, 23, w1, 32, 2000, 0, 0);
PlayNote(1, 27, w2, 32, 2300, 0, 0);
PlayNote(2, 30, w3, 32, 2600, 0, 0);
PlayNote(3, 35, w1, 32, 2900, 0, 0);
FinishAudio();
return(0);
} /* end of main() */
InitAudio()
{
int error,i;
/* Declare all message blocks available */
for(i=0; i<AUDBUFFERS; i++) { inuse[i] = NO; }
/* Open device but don't allocate channels */
openIOB.ioa_Length = 0; /* (no allocation table) */
error = OpenDevice("audio.device",0,&openIOB,0);
if(error) finishup ("audio device won't open!");
/* Get the device address for later use */
device = openIOB.ioa_Request.io_Device;
/* Create ports for replies from each channel as well as
* one port to be used for the control and synchonous functions */
for(i=0; i<4; i++)
{ auReplyPort = CreatePort(0,0);
replyPort[i] = auReplyPort;
if(auReplyPort == 0) finishup("cannot create a port!");
chipaudio[i] = 0; /* have not yet created the waves */
datalength[i] = 1; /* used for custom sound samples */
}
controlPort = CreatePort(0,0);
if(controlPort == 0) finishup("can't create control port");
error = MakeWaves();
if(error == -1) finishup("waves won't fit in RAM!");
for(i=0; i<4; i++)
{ dynamix[i] = 0; } /* no dynamic I/O blocks allocated
* for any channel thus far */
return(0);
}
FinishAudio()
{
LONG i;
struct ExtIOB *iob;
for(i=0; i<AUDBUFFERS; i++)
{ if(inuse[i] == YES)
{ /* make sure all global blocks are done */
WaitIO(&audbuffer[i]);
}
}
#ifdef DEBUG
printf("All global I/O blocks are done\n");
printf("channels 0,1,2,3 have %ld,%ld,%ld,%ld blocks in play\n",
dynamix[0], dynamix[1], dynamix[2], dynamix[3]);
#endif DEBUG
for(i=0; i<4; i++)
{ if(dynamix[i]) /* If this channel still playing a */
/* dynamically allocated block, wait */
/* for all messages to return before */
/* the program exits. */
{
emptyit:
iob = (struct ExtIOB *)GetMsg(replyPort[i]);
if(iob == 0 && dynamix[i] != 0) /* if no message arrived... */
{ WaitPort(replyPort); /* wait for I/O done */
goto emptyit; /* and empty the port */
}
FreeIOB(iob,i);
if(dynamix[i] != 0) goto emptyit;
}
}
for(i=0; i<4; i++) FreeChannel(i);
finishup("Done!\n");
return(0);
}
finishup(string)
char *string;
{
int i;
if(device) CloseDevice(&openIOB);
printf("closed the device\n");
for(i=0; i<4; i++)
{ if(chipaudio[i]) FreeMem(chipaudio[i],WAVES_TOTAL);
if(replyPort[i])
DeletePort(replyPort[i]);
}
if(controlPort) DeletePort(controlPort);
printf("%ls\n",string);
exit(0);
return(0);
}
int
ControlChannel(channel, command)
WORD channel;
WORD command;
{
LONG rtn;
struct ExtIOB *iob, controlIOB;
iob = &controlIOB;
iob->ioa_Request.io_Device = device;
iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
InitBlock(iob,channel); /* init it for CMD_WRITE, then change */
iob->ioa_Request.io_Command = command;
iob->ioa_Request.io_Flags = IOF_QUICK;
BeginIO(iob);
WaitIO(iob);
rtn = ((LONG)(iob->ioa_Request.io_Error));
return(rtn);
}
struct ExtIOB *
GetIOB(ch)
LONG ch;
{
WORD i,use_reply;
struct ExtIOB *iob; /* in case we need to allocate one */
ReEmployIOB(); /* find already used ones and free them */
/* so that when we do a get... */
if(ch == -1) use_reply = 0; /* which reply port to use */
else use_reply = ch;
for(i=0; i<AUDBUFFERS; i++)
{ if(inuse[i] == NO)
{ inuse[i] = YES;
audbuffer[i].ioa_Request.io_Device = device;
audbuffer[i].ioa_Request.io_Message.mn_ReplyPort =
replyPort[use_reply];
audbuffer[i].ioa_Request.io_Message.mn_Length = i;
audbuffer[i].ioa_Request.io_Message.mn_Node.ln_Name =
globalname;
#ifdef DEBUG
printf("Using a global iob\n");
#endif DEBUG
return(&audbuffer[i]);
}
}
/* if all globals are in use, have to allocate one */
iob = (struct ExtIOB *)AllocMem(sizeof(struct ExtIOB),
MEMF_CLEAR);
if(iob == 0) return(0); /* out of memory */
else
{ iob->ioa_Request.io_Device = device;
iob->ioa_Request.io_Message.mn_ReplyPort =
replyPort[use_reply];
iob->ioa_Request.io_Message.mn_Node.ln_Name =
dynamicname;
iob->ioa_Request.io_Message.mn_Length = dynamix[use_reply];
dynamix[use_reply] += 1; /* add one to number allocated
* for a specific channel */
#ifdef DEBUG
printf("Allocated a new dynamic iob\n");
#endif DEBUG
return(iob);
}
return(0);
}
/* ReEmployIOB - look at all of the reply ports and if any IOBs
* hanging around with nothing to do, free them.
*/
ReEmployIOB()
{
LONG i;
struct MsgPort *mp;
struct ExtIOB *iob;
for(i=0; i<4; i++) /* remove all iob's from all ports */
{ mp = replyPort[i];
while( (iob = (struct ExtIOB *)GetMsg(mp)) != 0)
{
#ifdef DEBUG
printf("type of iob freed is: %ls\n",
iob->ioa_Request.io_Message.mn_Node.ln_Name);
printf("its identifier value is: %ld\n",
iob->ioa_Request.io_Message.mn_Length);
#endif DEBUG
FreeIOB(iob, i);
}
}
return(0);
}
/* Free a global or an allocated IOB */
int
FreeIOB(iob, ch)
struct ExtIOB *iob;
LONG ch; /* which channel was it attached to? */
{
WORD i;
if(iob->ioa_Request.io_Message.mn_Node.ln_Name == dynamicname)
{ FreeMem(iob, sizeof(struct ExtIOB));
if(dynamix[ch]) dynamix[ch] -= 1; /* subtract one if nonzero */
return(0L);
}
else if(iob->ioa_Request.io_Message.mn_Node.ln_Name == globalname)
{ i = iob->ioa_Request.io_Message.mn_Length;
#ifdef DEBUG
printf("Freeing global buffer numbered %ld\n",i);
#endif DEBUG
if(i < AUDBUFFERS)
{ inuse[i] = NO; /* frees this one for reuse */
}
return(0L);
}
/* if get here, the names don't match... something is wrong.*/
else { printf("FreeIOB: names don't match...unknown error\n");
return(-1); /* unknown source of IOB fed to routine. */
}
return(0);
}
/* Initialize an audio I/O block for default CMD_WRITE operation. */
int
InitBlock(iob, channel)
struct ExtIOB *iob;
WORD channel;
{
/* Device and ReplyPort fields have been initialized by GetIOB */
iob->ioa_Request.io_Unit = unit[channel];
/* Allocation key */
iob->ioa_AllocKey = key[channel];
/* Where is the waveform? Just be sure is in MEMF_CHIP!!! */
/* USER initializes datalength[ch] before calling this; */
/* for sampled sound command write operation. */
iob->ioa_Data = chipaudio[channel];
iob->ioa_Length = datalength[channel];
/* Another routine, must initialize:
period ioa_Period
volume ioa_Volume
cycles ioa_Cycles
message ioa_WriteMessage
*/
/* Default command type is CMD_WRITE */
iob->ioa_Request.io_Command = CMD_WRITE;
/* If IOF_QUICK is zeroed, this would affect the
* period and volume. If a CMD_WRITE, it queues if
* another note is already playing. We queue CMD_WRITES.
*/
iob->ioa_Request.io_Flags = ADIOF_PERVOL;
return(0);
}
/* To request "any" stereo pair, use pair = -1;
* To request a specific stereo pair, use pair = {0, 1, 2 or 3}
* corresponding to channels 0 and 1, 0 and 2, 1 and 2 or 1 and 3
* respectively.
*/
int
GetStereoPair(pair)
LONG pair;
{
int error, value;
struct ExtIOB *iob, controlIOB;
iob = &controlIOB;
iob->ioa_Request.io_Device = device;
iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
InitBlock(iob,0); /* init it for CMD_WRITE, then change */
/* set precedence of the request for a channel */
iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
/* Type of command is ALLOCATE */
iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
if(pair == -1)
{ /* Point to the allocation map */
iob->ioa_Data = (UBYTE *)stereostuff;
/* It contains 4 entries */
iob->ioa_Length = 4;
}
else if(pair >=0 && pair <= 3)
{ iob->ioa_Data = (UBYTE *)(&stereostuff[pair]);
iob->ioa_Length = 1;
}
else /* chose a bad channel pair; cannot allocate it */
{
return(-1);
}
/* Don't wait for allocation, channels
* should be available! If we don't set
* ADIOF_NOWAIT, the task will idle waiting
* for a chance to allocate the channel,
* looking again each time another task
* allocates or frees a channel.
*/
iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
BeginIO(iob);
error = WaitIO(iob); /* returns nonzero if error */
if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
{ /* if flag not set, then the message
* was appended to the reply port
* (was not quick I/O after all) */
GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
}
if(error)
{ return(-1);
}
/* Save the values... freeing the IOB on exit */
gotunit = (iob->ioa_Request.io_Unit);
gotkey = (iob->ioa_AllocKey);
switch((LONG)(iob->ioa_Request.io_Unit))
{ case 3: value = 0; break;
case 5: value = 1; break;
case 10: value = 2; break;
case 12: value = 3; break;
default: value = -1; break;
}
return(value);
}
/* To request "any" channel, use ch = -1;
* To request a specific channel, use ch = {0, 1, 2 or 3};
*/
int
GetChannel(ch)
LONG ch;
{
int error, value;
struct ExtIOB *iob, controlIOB;
iob = &controlIOB;
iob->ioa_Request.io_Device = device;
iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
InitBlock(iob,0); /* init it for CMD_WRITE, then change */
iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
if(ch == -1)
{ iob->ioa_Data = (UBYTE *)anychan;
iob->ioa_Length = 4;
}
else if(ch >=0 && ch <= 3)
{ iob->ioa_Data = (UBYTE *)(&anychan[ch]);
iob->ioa_Length = 1;
}
else /* chose a bad channel number; cannot allocate it */
{ return(-1);
}
iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
BeginIO(iob);
error = WaitIO(iob); /* returns nonzero if error */
if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
{ GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
}
if(error)
{ return(-1);
}
gotunit = (iob->ioa_Request.io_Unit);
gotkey = (iob->ioa_AllocKey);
switch((LONG)(iob->ioa_Request.io_Unit))
{ case 1: value = 0; break;
case 2: value = 1; break;
case 4: value = 2; break;
case 8: value = 3; break;
default: value = -1; break;
}
return(value);
}
int
FreeChannel(ch)
LONG ch;
{
int error;
struct ExtIOB *iob, controlIOB;
iob = &controlIOB;
iob->ioa_Request.io_Device = device;
iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
InitBlock(iob,ch); /* init it for CMD_WRITE, then change */
/* (pick up unit and key value for channel) */
iob->ioa_Request.io_Command = ADCMD_FREE;
iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
BeginIO(iob);
error = WaitIO(iob); /* returns nonzero if error */
if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
{ GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
}
if(error)
{ return(-1);
}
return(0);
}
/* NOTE: FreeChannel should work as FreeStereoPair(pair) too! */
/* THE FOLLOWING ROUTINES ARE PARAPHRASED FROM A USENET and BIX
* POSTING MADE IN 1985 BY STEVEN A. BENNETT.
*/
/* I have modified his routines to queue the audio commands in
* place of starting forever-duration and canceling each note.
* Many of his original comments have been incorporated into
* the article.
*/
/* PlayNote(...) */
/* Starts a sound on the channel with specified period and volume. */
/* This nice little routine takes a note and plays it on the given
* voice. The note is basically an integer from
* 0 to 11 (c to b) plus 12 per octave above the first and lowest.
*
* The waveform to use is determined by adding an index (woffsets[])
* dependant on the octave.
*
* The length of the waveform (in wlen[]) is likewise dependant on
* the octave. Note that octaves start with zero, not one.
*/
int
PlayNote(channel, note, wf, vol, duration, priority, message)
char *wf; /* waveform to use */
LONG vol, channel, duration, note; /* specific note number */
LONG priority;
struct Message *message;
{
LONG per, len, oct; /* period, length of waveform, which octave */
char *wavepointer; /* where to find start of waveform */
struct ExtIOB *iob;
int frequency;
iob = GetIOB(channel);
if(iob != 0)
{
InitBlock(iob, channel); /* set up for CMD_WRITE */
oct = note / 12;
wavepointer = wf + woffsets[oct];
len = wlen[oct];
per = perval[note % 12];
/* Set the parameters */
iob->ioa_Data = (UBYTE *)wavepointer;
iob->ioa_Length = len;
iob->ioa_Period = per;
iob->ioa_Volume = vol;
/* PlayNote (continued) */
/* Look at the frequency that it is to play by backwards calc. */
frequency = 3579545 / (len * per);
/* Calculate cycles from duration in 1000ths of a second */
/* Multiply all-in-one to maintain max precision possible */
/* (all integer arithmetic.) */
iob->ioa_Cycles = ((LONG)(frequency * duration)/1000);
BeginIO(iob);
return(0); /* all went ok */
}
else
{ return(-1); /* couldnt get IOB */
}
return(0);
}
/* SetPV(channel, per, vol)
* int channel, per, vol;
*/
int
SetPV(channel, per, vol)
int channel, per, vol;
{
int error;
struct ExtIOB *iob, controlIOB;
iob = &controlIOB;
iob->ioa_Request.io_Device = device;
iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
InitBlock(iob, channel); /* set up for CMD_WRITE */
iob->ioa_Period = per;
iob->ioa_Volume = vol;
iob->ioa_Request.io_Command = ADCMD_PERVOL;
iob->ioa_Request.io_Flags = IOF_QUICK | ADIOF_PERVOL;
BeginIO(iob); /* This one will be synchronous; affects whatever
* is playing on this channel at this time.
*/
error = WaitIO(iob); /* OK to wait, since it will return */
return(error); /* copy of io_Error field; should be 0 */
}
/* SetWaves(w1, w2, w3): create first sawtooth, triangle and square wave */
SetWaves(w1, w2, w3)
UBYTE *w1, *w2, *w3;
{
int i, increment, value, sqvalue;
value = 0; increment = 2;
sqvalue = 127;
for (i = 0; i < BIG_WAVE; ++i)
{
w1[i] = i; /* do the sawtooth */
if(i > 62 && i < 180) increment = -2;
else
if(i >= 180) increment = 2;
w2[i] = value; value += increment; /* triangle wave */
if(i > 126) sqvalue = -127;
w3[i] = sqvalue;
}
return(0);
}
/* ExpandWave(wfp) - replicate waves in decreasing sample sizes
* BYTE *wfp;
*/
ExpandWave(wfp)
BYTE *wfp;
{
int i, j, rate;
BYTE *tptr;
rate = 1;
tptr = wfp + BIG_WAVE;
for (i = 0; i < NBR_WAVES - 1; ++i)
{
rate *= 2;
for (j = 0; j < BIG_WAVE; j += rate)
*tptr++ = wfp[j];
}
return(0);
}
/* MakeWaves()
*
* Just makes a sawtooth, triangle and square wave in chip mem
* and expands them.
*/
int
MakeWaves()
{
/* allocate the memory for the waveforms.
*/
w1 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
w2 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
w3 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
if (w1 == NULL || w2 == NULL || w3 == NULL)
return(-1); /* ran out of memory! */
/* get and expand the waveforms */
SetWaves(w1, w2, w3);
ExpandWave(w1); chipaudio[0]=w1;
ExpandWave(w2); chipaudio[1]=w2;
ExpandWave(w3); chipaudio[2]=w3;
return(0);
}